package com.vmock.base.filter;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.SetUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.AbstractHandlerMethodMapping;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;

/**
 * 主要业务过滤器
 *
 * @author vt
 * @since 2019年12月4日
 */
@Slf4j
public class MockFilter implements Filter {

    /**
     * 逻辑处理类
     */
    private MockFilterHandler mockFilterHandler;

    /**
     * bean 辅助
     */
    private ApplicationContext applicationContext;

    private Set<String> requestMappings = new HashSet<>(500);

    private Set<String> staticResources = new HashSet<>();

    /**
     * 初始化
     *
     * @param filterConfig 配置
     */
    @Override
    public void init(FilterConfig filterConfig) {
        ServletContext servletContext = filterConfig.getServletContext();
        // init applicationContext
        applicationContext = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
        // init mockFilterHandler
        mockFilterHandler = applicationContext.getBean(MockFilterHandler.class);

        AbstractHandlerMethodMapping<RequestMappingInfo> methodMapping = (AbstractHandlerMethodMapping<RequestMappingInfo>) applicationContext.getBean("requestMappingHandlerMapping");
        Map<RequestMappingInfo, HandlerMethod> mapRet = methodMapping.getHandlerMethods();
//        mapRet.keySet().forEach(requestMappingInfo -> requestMappings.addAll(requestMappingInfo.getPatternsCondition().getPatterns()));
        mapRet.keySet().forEach(requestMappingInfo -> requestMappings.addAll(trimParam(requestMappingInfo.getPatternsCondition().getPatterns())));
        log.debug("request mappings:{}", requestMappings);
        staticResources.addAll(Arrays.asList(new String[]{".ico", ".html", ".jpg", ".png", ".css", ".js", ".map", ".ttf", ".woff", ".woff2", ".gif"}));
    }

    private List<String> trimParam(Set<String> origin) {
        List<String> result = new ArrayList<>();

        origin.forEach(o -> {
            String r = StringUtils.replaceAll(o, "\\{[^\\}]+\\}", "");
            result.add(r);
        });
        return result;
    }

    /**
     * 业务实现
     *
     * @param servletRequest
     * @param servletResponse
     * @param filterChain
     */
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        // 转换类型
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;

        if (!needMock(httpServletRequest)) {
            filterChain.doFilter(servletRequest, servletResponse);
            return;
        }

        setCorsHeader(httpServletRequest, httpServletResponse);

        // 执行主要逻辑处理
        mockFilterHandler.execute(httpServletRequest, httpServletResponse);
    }

    private boolean needMock(HttpServletRequest request) {
        String uri = request.getRequestURI();
        if (requestMappings.stream().anyMatch(u -> uri.startsWith(u))) {
            return false;
        }

        if (staticResources.stream().anyMatch(uri::endsWith)) {
            return false;
        }
        if (uri.equals("/")) {
            return false;
        }

        return true;
    }

    /**
     * 设置filter跨域header
     *
     * @param request  请求
     * @param response 响应
     */
    private static void setCorsHeader(HttpServletRequest request, HttpServletResponse response) {
        // 域
        response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
        // credentials
        response.setHeader("Access-Control-Allow-Credentials", "true");
        // method
        response.setHeader("Access-Control-Allow-Methods", "*");
        // age
        response.setHeader("Access-Control-Max-Age", "3600");
        // header
        response.setHeader("Access-Control-Allow-Headers", "*");
    }
}
